package cn.android666.audiorecorder.aac

import android.media.MediaCodec
import android.media.MediaCodecInfo
import android.media.MediaFormat
import cn.android666.audiorecorder.objectpool.ByteBufferPool
import timber.log.Timber
import java.nio.ByteBuffer

/**
 * AAC编码器，可以把PCM音频编码AAC
 * @param sampleRateInHz 指定PCM音频数据的采样率
 * @param channelCount 指定PCM音频数据的通道数量
 * @param maxInputSize 指定每次送入AAC编码器的PCM音频数据的最大大小
 */
class AACEncoder(private val sampleRateInHz: Int, private val channelCount: Int, maxInputSize: Int, private val pcmByteBufferPool: ByteBufferPool) {

    private val mMediaCodec: MediaCodec
    private var inputBufferIndex: Int = MediaCodec.INFO_TRY_AGAIN_LATER
    private var outputBufferIndex: Int = MediaCodec.INFO_TRY_AGAIN_LATER
    private lateinit var inputBuffer: ByteBuffer
    private lateinit var outputBuffer: ByteBuffer
//    private val mBitrate = 32000    // 比特率
    private val mBitrate = 128000    // 比特率，当采样率为44100时，比特率设置为128k比较合适，此时在看aac音频波形几乎和pcm一模一样。如果比特率设置为32k,则发现波形相差较大，听声音也能明显感觉到变差了很多。
    private var mBufferInfo = MediaCodec.BufferInfo()
    private lateinit var aacByteBufferPool: ByteBufferPool
    private lateinit var aacSaveThread: AACSaveThread
//    private val timeoutUs = 10_000L
    private val timeoutUs = 0L

    init {
        val mime = MediaFormat.MIMETYPE_AUDIO_AAC
        mMediaCodec = MediaCodec.createEncoderByType(mime)
        val mediaFormat = MediaFormat.createAudioFormat(mime, sampleRateInHz, channelCount)
        mediaFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR) // 设置码率为动态码率，默认也是动态码率
        mediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC) // 设置profile为AAC LC
        mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, mBitrate) // 码率（即比特率）
        mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, maxInputSize)

        mMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
        mMediaCodec.start()
    }

    /** 把PCM格式的音频数据编码为AAC数据 */
    fun encodePCMToAAC(pcmByteBuffer: ByteBuffer) {
        // 1、把pcm数据放入编码器中进行编码
        putDataToEncoder(pcmByteBuffer)
        // 2、从编码器中取出编码后的数据
        getDataFromEncoder()
    }

    /** 把pcm数据放入编码器中进行编码 */
    private fun putDataToEncoder(pcmByteBuffer: ByteBuffer) {
        val flags = if (pcmByteBuffer.capacity() == 0) MediaCodec.BUFFER_FLAG_END_OF_STREAM else 0
        inputBufferIndex = mMediaCodec.dequeueInputBuffer(timeoutUs)

        if (inputBufferIndex >= 0) {
            inputBuffer = mMediaCodec.getInputBuffer(inputBufferIndex)!!
            inputBuffer.put(pcmByteBuffer)
            mMediaCodec.queueInputBuffer(inputBufferIndex, 0, pcmByteBuffer.limit(), System.nanoTime() / 1000, flags)
        }

        // pcmByteBuffer是从对象池中借的，使用结束后要还回去，空Buffer不是从池里借的，所以如果是空Buffer则不用还到池里。
        if (pcmByteBuffer.capacity() != 0) {
            pcmByteBufferPool.returnObject(pcmByteBuffer)
        }
    }

    /** 从编码器中取出编码后的数据 */
    private fun getDataFromEncoder() {
        while (mMediaCodec.dequeueOutputBuffer(mBufferInfo, timeoutUs).also { outputBufferIndex = it } >= 0) {
            if (mBufferInfo.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
                Timber.i("已经到达流的终点了")
                mMediaCodec.releaseOutputBuffer(outputBufferIndex, false)
                mMediaCodec.stop()
                mMediaCodec.release()
                aacSaveThread.close()
                break
            } else if (mBufferInfo.flags != MediaCodec.BUFFER_FLAG_CODEC_CONFIG) {
                outputBuffer = mMediaCodec.getOutputBuffer(outputBufferIndex)!!

                if (!::aacByteBufferPool.isInitialized) {
                    aacByteBufferPool = ByteBufferPool(outputBuffer.capacity(), "aacSaveThread")
                    aacSaveThread = AACSaveThread(aacByteBufferPool, sampleRateInHz, channelCount).also { it.start() }
                }

                aacByteBufferPool.borrowObject()?.let { aacByteBuffer ->
                    aacByteBuffer.put(outputBuffer)
                    aacByteBuffer.flip()
                    aacSaveThread.addAacBytes(aacByteBuffer)
                }
            } else {
                outputBuffer = mMediaCodec.getOutputBuffer(outputBufferIndex)!!
                Timber.i("下面输出配置信息，共${mBufferInfo.size}个字节-------------")
                while (outputBuffer.hasRemaining()) {
                    Timber.i(outputBuffer.get().toString(2))
                }
                Timber.i("---------------------------")
            }

            mMediaCodec.releaseOutputBuffer(outputBufferIndex, false) // 把缓存对象还给MediaCodec
        }
    }
}